/**
*  Copyright (c) 2011, Alex Theodoridis
*  All rights reserved.

*  Redistribution and use in source and binary forms, with
*  or without modification, are permitted provided that the
*  following conditions are met:
*  Redistributions of source code must retain the above
*  copyright notice, this list of conditions and the following disclaimer.
*  Redistributions in binary form must reproduce the above
*  copyright notice, this list of conditions and the following
*  disclaimer in the documentation and/or other materials
*  provided with the distribution.

*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
*  AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
*  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
*  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
*  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*/

#ifndef BEPNeuronH
#define BEPNeuronH
#include <NeuralNetwork/Neuron/INeuron.h>
#include <boost/numeric/conversion/cast.hpp>
#include <functional>

namespace nn {

namespace bp {

/*
* Represent the back error propagation Neuron trainer.
* This class holds a pointer to neuron which should
* be trained with back error propagation algorithm.
*/
template<class NeuronType>
class BPNeuron  : public INeuron<NeuronType> {
public:
    typedef nn::INeuron<NeuronType> Neuron;
    typedef typename Neuron::Var Var;
    typedef typename Neuron::OutputFunction OutputFunction;

    template<typename EquationType>
    struct rebind {
        typedef BPNeuron< typename NeuronType::template rebind<EquationType>::type > type;
    };

private:
    /**
    * Neurons error delta
    */
    Var m_delta;

    /**
     * Equation needed in order to calculate the differential value;
     */
    OutputFunction m_outputFunction;

public:

    /**
    * @brief Default constructor will initialize the BPNeuron with 0.
    */
    BPNeuron ():m_delta(boost::numeric_cast<Var>(0.f)) {}

    /**
    * @brief Initialization constructor
    * @param neuron the pointer to the neuron which need to be trained
    */
    BPNeuron ( unsigned int inputsNumber ) :m_delta(boost::numeric_cast<Var>(0.f)), Neuron ( inputsNumber ) {}

    /**
    * @brief Will return the errors delta for the trained neuron.
    * @returns the error deltas value.
    */
    const Var& getDelta ( void ) const {
        return m_delta;
    }

    void setDelta ( const Var& delta ) {
        m_delta = delta;
    }

    /*!
    *  Will calculate the differential value.
    *  @return the calculated value.
    */
    const Var& calculateDelta( const Var& expectedOutput, std::function< Var(const Var&, const Var&)> momentum ) {
        m_delta = momentum (m_delta, m_outputFunction.calculateDelta ( Neuron::getOutput(), expectedOutput ) );
        return m_delta;
    }

    const Var calculateDerivate()const {
        return m_outputFunction.calculateDerivate(Neuron::getOutput());
    }

    /**
    * Destructor.
    */
    ~BPNeuron() {}
};

}

}
//---------------------------------------------------------------------------
#endif
